﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace FGK
{
    class MapaFotonow
    {
        private Wezel root;
        private Wezel root2;
        public List<Foton> fotony;
        public List<Foton> kaustyka;

        public MapaFotonow()
        {
            fotony= new List<Foton>();
            kaustyka= new List<Foton>();
        }

        public void UstawRoot()
        {
            Console.WriteLine("* konstruuję mapy fotonów...");
            root = BudujDrzewo(fotony, 0);
            root2 = BudujDrzewo(kaustyka, 0);
        }

        public Kolor GetKolor(Wektor kolizja, float d)
        {
            int ilefotonow=10000;
            List<Foton> h = new List<Foton>();
            znajdzFotony(h, kolizja, d * d, root);
            /*if (h.Count > ilefotonow)
            {
                h = h.OrderBy(x => x.D2).ToList();
                for (int i = 0; i < h.Count - ilefotonow; i++)
                { 
                     h.RemoveAt(h.Count - 1);
                }
            }*/
            if (h.Count > 0)
            {
                float r=0, g=0, b=0;
                for (int i = 0; i < h.Count; i++)
                {
                    r += h.ElementAt(i).Energia.R * h.ElementAt(i).Moc;
                    g += h.ElementAt(i).Energia.G * h.ElementAt(i).Moc;
                    b += h.ElementAt(i).Energia.B * h.ElementAt(i).Moc;
                }
                //float dzielnik = (4.0f/3.0f)*(float)Math.PI* (float)Math.Pow ( (double)(h.ElementAt(h.Count-1).D2), (double)(3.0/2.0));
                float dzielnik = 1;
                return new Kolor((r / fotony.Count) / dzielnik, (g / fotony.Count) / dzielnik, (b / fotony.Count) / dzielnik);
            }
            else return new Kolor(0, 0, 0);
        }

        public Kolor GetKaustyka(Wektor kolizja, float d)
        {
            int ilefotonow = 5000;
            List<Foton> h = new List<Foton>();
            znajdzFotony(h, kolizja, d * d, root2);
            /*if (h.Count > ilefotonow)
            {
                h = h.OrderBy(x => x.D2).ToList();
                for (int i = 0; i < h.Count - ilefotonow; i++)
                {
                    h.RemoveAt(h.Count - 1);
                }
            }*/
            if (h.Count > 0)
            {
                float r = 0, g = 0, b = 0;
                for (int i = 0; i < h.Count; i++)
                {
                    r += h.ElementAt(i).Energia.R * h.ElementAt(i).Moc;
                    g += h.ElementAt(i).Energia.G * h.ElementAt(i).Moc;
                    b += h.ElementAt(i).Energia.B * h.ElementAt(i).Moc;
                }
                //float dzielnik = (4.0f / 3.0f) * (float)Math.PI * (float)Math.Pow((double)(h.ElementAt(h.Count - 1).D2), (double)(3.0 / 2.0));
                float dzielnik = 0.1f;
                return new Kolor((r / kaustyka.Count) / dzielnik, (g / kaustyka.Count) / dzielnik, (b / kaustyka.Count) / dzielnik);
            }
            else return new Kolor(0, 0, 0);
        }

        public float odlegloscPlaszczyznyPodzialu(Wektor kolizja, Wezel r)
        {
            if (r.flaga % 3 == 0) return kolizja.X - r.wezel.Pozycja.X;
            else if (r.flaga % 3 == 1) return kolizja.Y - r.wezel.Pozycja.Y;
            else return kolizja.Z - r.wezel.Pozycja.Z;
        }

        public void znajdzFotony(List<Foton> h, Wektor kolizja, float d2, Wezel r)
        {
            if (r != null)
            {
                float dystans = odlegloscPlaszczyznyPodzialu(kolizja, r);
                if (dystans < 0)
                {
                    //jesteśmy na lewo, sprawdzamy wpierw lewe
                    znajdzFotony(h, kolizja, d2, r.lewy);
                    //sprawdźmy prawe
                    if (dystans * dystans < d2)
                    {
                        znajdzFotony(h, kolizja, d2, r.prawy);
                    }
                }
                else
                {
                    znajdzFotony(h, kolizja, d2, r.prawy);
                    if (dystans * dystans < d2)
                    {
                        znajdzFotony(h, kolizja, d2, r.lewy);
                    }
                }
                float q = (r.wezel.Pozycja.X - kolizja.X) * (r.wezel.Pozycja.X - kolizja.X) + (r.wezel.Pozycja.Y - kolizja.Y) * (r.wezel.Pozycja.Y - kolizja.Y) + (r.wezel.Pozycja.Z - kolizja.Z) * (r.wezel.Pozycja.Z - kolizja.Z);
                if (q < d2)
                {

                    h.Add(r.wezel);
                    h.ElementAt(h.Count - 1).D2 = q;
                }
            }

        }

        public Wezel BudujDrzewo(List<Foton> stos, int poziom)
        {
            if (stos.Count == 0) return null;
            else if (stos.Count == 1)
            {
                return new Wezel(stos.ElementAt(0), null, null, poziom);
            }
            else
            {
                int srodek = stos.Count / 2;
                if (poziom % 3 == 0) stos=stos.OrderBy(x => x.Pozycja.X).ToList();
                else if (poziom % 3 == 1) stos=stos.OrderBy(x => x.Pozycja.Y).ToList();
                else stos=stos.OrderBy(x => x.Pozycja.Z).ToList();
                List<Foton> listaLewa = new List<Foton>();
                List<Foton> listaPrawa = new List<Foton>();
                for (int i = 0; i < srodek - 1; i++)
                {
                    listaLewa.Add(stos.ElementAt(i));
                }
                for (int i = srodek; i < stos.Count; i++)
                {
                    listaPrawa.Add(stos.ElementAt(i));
                }
                return new Wezel(stos.ElementAt(srodek - 1), BudujDrzewo(listaLewa, poziom+1), BudujDrzewo(listaPrawa, poziom+1), poziom);
            }
        }

        public void add(Foton f)
        {
            fotony.Add(f);
        }

        public void add2(Foton f)
        {
            kaustyka.Add(f);
        }

        public Foton get(int p)
        {
            return fotony.ElementAt(p);
        }

    }
}
